using Microsoft.AspNetCore.Mvc.Filters;//IAsyncActionFilter(自带的接口)、ActionExecutingContext(context参数)(实现接口的异步方法和判断是否写审计日志方法都会用到)、ActionExecutionDelegate(next参数)、
using System.Threading.Tasks; //实现接口的异步方法
using webapi_Domain.Entity;
using webapi_Domain.Repository;
using System;//DateTime、Exception、Convert、Console
using System.Diagnostics;//Stopwatch
using Newtonsoft.Json;//JsonConvert
using Microsoft.AspNetCore.Mvc;//ObjectResult、JsonResult、ContentResult、
using Microsoft.AspNetCore.Mvc.Controllers;//ControllerActionDescriptor


namespace webapi.AuditedAttribute
{
    public class AuditActionFilter: IAsyncActionFilter
    {
        //存放审计日志的数据库表
        private readonly IRepositoryBase<AuditInfo> _auditInfo;
        
        //依赖注入审计日志表
        public AuditActionFilter(IRepositoryBase<AuditInfo> auditInfo) 
        {
            //拿到审计表
            _auditInfo = auditInfo;
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 
        {
            // 判断是否写日志
            // if (!ShouldSaveAudit(context)) {
            //     await next();
            //     return;
            // }

			//接口Type
            var type = (context.ActionDescriptor as ControllerActionDescriptor).ControllerTypeInfo.AsType();
            //方法信息
            var method = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;
            //方法参数
            var arguments = context.ActionArguments;
            //开始计时
            var stopwatch = Stopwatch.StartNew();

            //创建
            var auditInfo = new AuditInfo {
                UserInfo = "第一个审计人",
                ServiceName = type != null ? type.FullName : "",//来自于上面的接口type
                MethodName = method.Name,
                //Json化方法的请求参数
                Parameters = JsonConvert.SerializeObject(arguments),//ConvertArgumentsToJson(arguments)

                ExecutionTime = DateTime.Now,

                ClientName = context.HttpContext.Connection.LocalPort.ToString(),
                ClientIpAddress = context.HttpContext.Connection.RemoteIpAddress.ToString(),
                BrowserInfo = context.HttpContext.Request.Headers["User-Agent"],//context.HttpContext.Request.Path,
                //CustomData
            };
            
            //定义一个方法执行结果所有信息的类型对象(不仅仅是返回值,还有执行过程中的异常等等)
            ActionExecutedContext result = null;
            try {
                result = await next();
                //执行过程因为代码问题产生的异常
                if (result.Exception != null && !result.ExceptionHandled) {
                    auditInfo.Exception = result.Exception.ToString();
                    
                } 
            } catch (Exception ex) {
                //系统异常
                    auditInfo.Exception = ex.ToString();
                    throw;
            } finally {
                stopwatch.Stop();//方法结束,停止计时
                auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);//消耗了多少时间
                
                //为方法的返回值ReturnValue属性赋值
                if (result != null) {
                    switch (result.Result) {
                        case ObjectResult objectResult:
                            auditInfo.ReturnValue = JsonConvert.SerializeObject(objectResult.Value);
                            break;

                        case JsonResult jsonResult:
                            auditInfo.ReturnValue = JsonConvert.SerializeObject(jsonResult.Value);
                            break;

                        case ContentResult contentResult:
                            auditInfo.ReturnValue = contentResult.Content;
                            break;
                    }
                }
                Console.WriteLine("auditInfo".ToString());                
                
                //保存审计日志
                await _auditInfo.SaveAsync(auditInfo);
            }
        } 

        
        //判断是否写审计日志
        // private bool ShouldSaveAudit(ActionExecutingContext context)
        // {
        //     if (!(context.ActionDescriptor is ControllerActionDescriptor))
        //         return false;
        //     var methodInfo = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;

        //     if (methodInfo == null)
        //     {
        //         return false;
        //     }

        //     if (!methodInfo.IsPublic)
        //     {
        //         return false;
        //     }
        //     return true;
        // }
    }
}